#include "system.h"
#include "..\CGraphics\CGraphics.h"
#include <math.h>

void InitSystem() {
	TABELMHS = CreateEmptyTabelMhs();
	TABELMK = CreateEmptyTabelMK();
	TABELPMK = CreateEmptyTabelPMK();
	NODELIST = CreateEmptyNodeList();
	EDGELIST = CreateEmptyEdgeList();
	SOURCEFILE = UNDEFINEDFILE;
	ChangeModus(PERSIAPAN);
}

void GetUserInput() {
	StartPrintInput();
	if(MODUS==PERSIAPAN) {
		SetColor(FCI); printf("> "); SetColor(FWI); ReadArguments(NULL);
		ParseCommand_PersiapanMode(CReader);
	}
	else {
		SetColor(FCI); printf("%s >> ", NODENAME(ACTIVENODE).s); SetColor(FWI); ReadArguments(NULL);
		ParseCommand_SimulationMode(CReader);
	}
}

void ParseCommand_SimulationMode(Reader R) {
	if(R.nArgument == 0) {
		StartPrintMsg(); printf("Perintah kosong!\n");
	}
	else if(IsStringEqual(R.argument[0],RETRIEVE_COMMAND)) {
		ParseRetrieveCommand( (R.nArgument>1 && IsStringEqual(R.argument[1],CharToString("details"))) ? true : false );
	}
	else if(IsStringEqual(R.argument[0],INSERT_COMMAND)) {
		ParseInsertCommand();
	}
	else if(IsStringEqual(R.argument[0],UPDATE_COMMAND)) {
		ParseUpdateCommand();
	}
	else if(IsStringEqual(R.argument[0],DELETE_COMMAND)) {
		ParseDeleteCommand();
	}
	else if(IsStringEqual(R.argument[0],HELP__COMMAND)) {
		ParseHelpCommand_SimulationMode();
	}
	else if(IsStringEqual(R.argument[0],EXIT_COMMAND) || IsStringEqual(R.argument[0],CLOSE_COMMAND)) {
		ParseExitCommand_SimulationMode();
	}
	else {
		StartPrintMsg(); SetColor(FRI); printf("ERROR : Perintah tidak dikenali\n");
	}
}

void ChangeModus(int modus) {
	MODUS = modus;
	InitCGraphics(MODUS);
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// INSERT
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void ParseInsertCommand() {
	int selectedTable = 0; // 1 = mahasiswa, 2 = matakuliah, 3 = pengambilanMK, 0 = undefined
	
	while(selectedTable==0) {
		StartPrintProcess(); SetColor(FWI);
		printf("Nama Tabel : "); ReadArguments(NULL);
		if(CReader.nArgument==0) {
			StartPrintMsg(); SetColor(FRI); puts("ERROR : Perintah kosong!");
		}
		else if(IsStringEqual(ArgumentsToString(CReader),NAMATABELMHS)) selectedTable = 1;
		else if(IsStringEqual(ArgumentsToString(CReader),NAMATABELMK)) selectedTable = 2;
		else if(IsStringEqual(ArgumentsToString(CReader),NAMATABELPMK)) selectedTable = 3;
		else {
			StartPrintMsg(); SetColor(FRI); puts("ERROR : Nama tabel tidak dikenali");
			selectedTable = 0;
		}
	}
	
	switch(selectedTable) {
		case 1 : ParseInsertCommand_TabelMhs(); break;
		case 2 : ParseInsertCommand_TabelMK(); break;
		case 3 : ParseInsertCommand_TabelPMK(); break;
	}
}

void ParseInsertCommand_TabelMhs() {
	// EARLY RETURN
	nodeAddress N;
	TRAVERSETOEND(N,FIRST(NODELIST))
		if(HAS_SERVER(N) && HAS_TMHS(N)) break;
	if(!HAS_SERVER(N) || !HAS_TMHS(N)) { StartPrintMsg(); SetColor(FYI); printf("Tidak ada lokasi untuk menginsert"); return; }
	
	AddressMhs Mhs;
	string NIM;
	string nama;
	date TglLahir;
	string kota;
	nodeAddress to;
	bool done;
	
	char path[100];
	float time;

	ContinuePrintProcess(); SetColor(FWI); printf("NIM : "); ReadArguments(NULL); NIM = ArgumentsToString(CReader);
	while(CReader.nArgument==0) {
		StartPrintMsg(); SetColor(FRI); puts("ERROR : Tidak ada input!");
		RestartPrintProcess(); SetColor(FWI); printf("NIM : "); ReadArguments(NULL); NIM = ArgumentsToString(CReader);
	}
	Mhs = SearchRecordMhsByNIM(NIM,&TABELMHS);
	if(Mhs) {
		StartPrintMsg(); SetColor(FYI); puts("Record Mahasiswa sudah ada, Record ini akan direplikasi ke server pilihan Anda");
	}
	else {
		ContinuePrintProcess(); SetColor(FWI); printf("Nama : "); ReadArguments(NULL); nama = ArgumentsToString(CReader);
		ContinuePrintProcess();
		do {
			done = true;
			RestartPrintProcess(); SetColor(FWI); printf("Tanggal Lahir : "); ReadArguments(NULL); 
			string temp = ArgumentsToString(CReader);
			if(IsStringDate(temp)) TglLahir = StringToDate(CReader.argument[0]);
			else {
				StartPrintMsg(); SetColor(FRI); printf("ERROR : %s bukan tanggal", temp.s);
				done = false;
			}
		} while(!done);
		ContinuePrintProcess(); SetColor(FWI); printf("Kota Asal : "); ReadArguments(NULL); kota = ArgumentsToString(CReader);
		Mhs = AllocateRecordMhs(NIM, nama, TglLahir, kota);
	}
	ContinuePrintProcess(); SetColor(FWI); printf("Lokasi Insert : "); ReadArguments(NULL);
	to = SearchNodeByName(&NODELIST, ArgumentsToString(CReader));
	while(to==NULL || !HAS_SERVER(to) || !HAS_TMHS(to)) {
		StartPrintMsg(); SetColor(FRI); printf("ERROR : Server atau Tabel Mahasiswa tidak ditemukan");
		RestartPrintProcess(); SetColor(FWI);  printf("Lokasi Insert : "); ReadArguments(NULL);
		to = SearchNodeByName(&NODELIST, ArgumentsToString(CReader));
	}
	
	if(InsertRecordMhs(Mhs, &TABELMHS, to, ACTIVENODE, EDGELIST, &time, path)) {
		StartPrintMsg(); SetColor(FGI); printf("Insert record ke %s berhasil melalui %s dengan waktu %f detik", NODENAME(to).s, path, time);
	}
	else {
		StartPrintMsg(); SetColor(FRI); puts("Insert gagal!");
	}
}

void ParseInsertCommand_TabelMK() {
	// EARLY RETURN
	nodeAddress N;
	TRAVERSETOEND(N,FIRST(NODELIST))
		if(HAS_SERVER(N) && HAS_TMK(N)) break;
	if(!HAS_SERVER(N) || !HAS_TMK(N)) { StartPrintMsg(); SetColor(FYI); printf("Tidak ada lokasi untuk menginsert"); return; }
	
	AddressMK MK;
	string kode;
	string nama;
	string SKS;
	string bidang;
	nodeAddress to;
	bool valid;
	
	char path[100];
	float time;

	ContinuePrintProcess(); SetColor(FWI); printf("Kode MK : "); ReadArguments(NULL); kode = ArgumentsToString(CReader);
	while(CReader.nArgument==0) {
		StartPrintMsg(); puts("ERROR : Tidak ada input!");
		RestartPrintProcess(); SetColor(FWI); printf("Kode MK : "); ReadArguments(NULL); kode = ArgumentsToString(CReader);
	}
	MK = SearchRecordMKByKode(kode,&TABELMK);
	if(MK) {
		StartPrintMsg(); SetColor(FYI); puts("Record MataKuliah sudah ada, Record ini akan direplikasi ke server pilihan Anda");
	}
	else {
		ContinuePrintProcess(); SetColor(FWI); printf("Nama : "); ReadArguments(NULL); nama = ArgumentsToString(CReader);
		ContinuePrintProcess();
		do {
			valid = true;
			RestartPrintProcess(); SetColor(FWI);
			printf("SKS : "); ReadArguments(NULL); SKS = ArgumentsToString(CReader);
			valid = IsStringFloat(SKS);
			if(!valid) {
				StartPrintMsg(); SetColor(FRI); printf("ERROR : %s bukan angka", SKS.s);
			}
		} while(!valid);
		ContinuePrintProcess(); SetColor(FWI); printf("Bidang : "); ReadArguments(NULL); bidang = ArgumentsToString(CReader);
		MK = AllocateRecordMK(kode, nama, fabs(StringToInt(SKS)), bidang);
	}
	ContinuePrintProcess(); SetColor(FWI); printf("Lokasi Insert : "); ReadArguments(NULL);
	to = SearchNodeByName(&NODELIST, ArgumentsToString(CReader));
	while(to==NULL || !HAS_SERVER(to) || !HAS_TMK(to)) {
		StartPrintMsg(); printf("ERROR : Server atau Tabel MataKuliah tidak ditemukan");
		RestartPrintProcess(); SetColor(FWI); printf("Lokasi Insert : "); ReadArguments(NULL);
		to = SearchNodeByName(&NODELIST, ArgumentsToString(CReader));
	}
	
	if(InsertRecordMK(MK, &TABELMK, to, ACTIVENODE, EDGELIST, &time, path)) {
		StartPrintMsg(); SetColor(FGI); printf("Insert record ke %s berhasil melalui %s dengan waktu %f detik", NODENAME(to).s, path, time);
	}
	else {
		StartPrintMsg(); SetColor(FRI); puts("Insert gagal!");
	}
}

void ParseInsertCommand_TabelPMK() {
	// EARLY RETURN
	if(ISLINKEDLISTEMPTY(TABELMHS)||ISLINKEDLISTEMPTY(TABELMK)) {
		StartPrintMsg(); SetColor(FYI); printf("Database tidak memiliki data mahasiswa atau mata kuliah");
		return;
	}
	nodeAddress N;
	TRAVERSETOEND(N,FIRST(NODELIST))
		if(HAS_SERVER(N) && HAS_TPMK(N)) break;
	if(!HAS_SERVER(N) || !HAS_TPMK(N)) { StartPrintMsg(); SetColor(FYI); printf("Tidak ada lokasi untuk menginsert"); return; }
	
	
	AddressPMK PMK;
	AddressMhs Mhs;
	AddressMK MK;
	string Semester;
	string TA;
	string Nilai;
	bool valid;
	
	nodeAddress to;
	
	char path[100];
	float time;

	ContinuePrintProcess(); SetColor(FWI); printf("NIM : "); ReadArguments(NULL); Mhs = SearchRecordMhsByNIM(ArgumentsToString(CReader),&TABELMHS);
	while(Mhs==NULL) {
		StartPrintMsg(); puts("ERROR : Mahasiswa dengan NIM tersebut tidak ditemukan");
		RestartPrintProcess(); SetColor(FWI); printf("NIM : "); ReadArguments(NULL); Mhs = SearchRecordMhsByNIM(ArgumentsToString(CReader),&TABELMHS);
	}
	ContinuePrintProcess(); SetColor(FWI); printf("Kode MK : "); ReadArguments(NULL); MK = SearchRecordMKByKode(ArgumentsToString(CReader),&TABELMK);
	while(MK==NULL) {
		StartPrintMsg(); puts("ERROR : MataKuliah dengan kode tersebut tidak ditemukan!");
		RestartPrintProcess(); SetColor(FWI); printf("Kode MK : "); ReadArguments(NULL); MK = SearchRecordMKByKode(ArgumentsToString(CReader),&TABELMK);
	}
	ContinuePrintProcess(); 
	do {
		valid = true;
		RestartPrintProcess(); SetColor(FWI); printf("Semester : "); ReadArguments(NULL); Semester = ArgumentsToString(CReader);
		valid = IsStringFloat(Semester);
		if(!valid) {
			StartPrintMsg(); SetColor(FRI); printf("ERROR : %s bukan angka",Semester.s);
		}
	} while(!valid);
	ContinuePrintProcess(); 
	do {
		valid = true;
		RestartPrintProcess(); SetColor(FWI); printf("ThnAkademik : "); ReadArguments(NULL); TA = ArgumentsToString(CReader);
		valid = IsStringThnAkademik(TA);
		if(!valid) {
			StartPrintMsg(); SetColor(FRI); printf("ERROR : %s bukan ThnAkademik",TA.s);
		}
	} while(!valid);
	
	PMK = SearchRecordPMKByKey(Mhs, MK, StringToInt(Semester), StringToThnAkademik(TA), &TABELPMK);
	if(PMK!=NULL) {
		StartPrintMsg(); SetColor(FYI); puts("Record PengambilanMK sudah ada. Record akan direplikasi ke server pilihan Anda");
	}
	else {
		ContinuePrintProcess();
		do {
			RestartPrintProcess(); SetColor(FWI); printf("Nilai : "); ReadArguments(NULL); Nilai = ArgumentsToString(CReader);
			valid = IsStringABCDE(Nilai);
			if(!valid) {
				StartPrintMsg(); printf("%s tidak valid, harus A,B,C,D, atau E", Nilai.s);
			}
		} while(!valid);
		PMK = AllocateRecordPMK(Mhs,MK,StringToInt(Semester),StringToThnAkademik(TA),UpperChar(*StringToChar(Nilai)));
	}
	ContinuePrintProcess(); SetColor(FWI); printf("Lokasi Insert : "); ReadArguments(NULL);
	to = SearchNodeByName(&NODELIST, ArgumentsToString(CReader));
	while(to==NULL || !HAS_SERVER(to) || !HAS_TPMK(to)) {
		StartPrintMsg(); printf("ERROR : Server atau Tabel PengambilanMK tidak ditemukan");
		RestartPrintProcess(); SetColor(FWI); printf("Lokasi Insert : "); ReadArguments(NULL);
		to = SearchNodeByName(&NODELIST, ArgumentsToString(CReader));
	}
	
	if(InsertRecordPMK(PMK, &TABELPMK, to, ACTIVENODE, EDGELIST, &time, path)) {
		StartPrintMsg(); SetColor(FGI); printf("Insert record ke %s berhasil melalui %s dengan waktu %f detik", NODENAME(to).s, path, time);
	}
	else {
		StartPrintMsg(); SetColor(FRI); puts("Insert gagal!");
	}
}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// RETRIEVE
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void ParseRetrieveCommand(bool detailsEnabled) {
	int selectedTable = 0; // 1 = mahasiswa, 2 = matakuliah, 3 = pengambilanMK, 0 = undefined
	
	while(selectedTable==0) {
		StartPrintProcess(); SetColor(FWI);
		printf("Nama Tabel : "); ReadArguments(NULL);
		if(CReader.nArgument==0) {
			StartPrintMsg(); SetColor(FRI); puts("ERROR : Perintah kosong!");
		}
		else if(IsStringEqual(ArgumentsToString(CReader),NAMATABELMHS)) selectedTable = 1;
		else if(IsStringEqual(ArgumentsToString(CReader),NAMATABELMK)) selectedTable = 2;
		else if(IsStringEqual(ArgumentsToString(CReader),NAMATABELPMK)) selectedTable = 3;
		else {
			StartPrintMsg(); SetColor(FRI); puts("ERROR : Nama tabel tidak dikenali");
			selectedTable = 0;
		}
	}
	
	switch(selectedTable) {
		case 1 : ParseRetrieveCommand_TabelMhs(detailsEnabled); break;
		case 2 : ParseRetrieveCommand_TabelMK(detailsEnabled); break;
		case 3 : ParseRetrieveCommand_TabelPMK(detailsEnabled); break;
	}
}

void ParseRetrieveCommand_TabelMhs(bool detailsEnabled) {
	// EARLY RETURN
	if(ISLINKEDLISTEMPTY(TABELMHS)) {
		StartPrintMsg(); SetColor(FYI); puts("Database tidak memiliki satupun Record Mahasiswa");
		return;
	}
	
	ContinuePrintProcess();
	float totalWaktu = 0;
	int count = 0;
	bool showNIM = false, showNama = false, showTglLahir = false, showKotaAsal = false, showIPK = false, showJmlSKS = false;
	bool done;
	do {
		done = true;
		RestartPrintProcess(); SetColor(FWI); printf("Kolom yang akan ditampilkan : "); ReadArguments(NULL);
		if(CReader.nArgument==0) {
			showNIM = true; showNama =true; showTglLahir = true; showKotaAsal = true; showIPK = true; showJmlSKS = true;
		}
		else {
			int i = 0;
			for(i;i<CReader.nArgument;i++) {
				if(IsStringEqual(CReader.argument[i], Kol_NIM)) showNIM = true;
				else if(IsStringEqual(CReader.argument[i], Kol_Nama)) showNama = true;
				else if(IsStringEqual(CReader.argument[i], Kol_TglLahir)) showTglLahir = true;
				else if(IsStringEqual(CReader.argument[i], Kol_KotaAsal)) showKotaAsal = true;
				else if(IsStringEqual(CReader.argument[i], Kol_IPK)) showIPK = true;
				else if(IsStringEqual(CReader.argument[i], Kol_JmlSKS)) showJmlSKS = true;
				else if(IsStringEqual(CReader.argument[i], CharToString("*"))) { 
					showNIM = true; showNama =true; showTglLahir = true; showKotaAsal = true; showIPK = true; showJmlSKS = true;
				}
				else {
					StartPrintMsg(); printf("ERROR : Kolom %s tidak dikenali\n", CReader.argument[i].s);
					showNIM = false; showNama = false; showTglLahir = false; showKotaAsal = false; showIPK = false; showJmlSKS = false;
					done = false;
				}
			}
		}
	} while(!done);
	ContinuePrintProcess();
	do {
		done = true;
		RestartPrintProcess(); printf("Kondisi pencarian : "); ReadArguments(NULL);
		AddressMhs M = FIRST(TABELMHS);
		if(M!=NULL) {
			TRAVERSETOEND2(M,FIRST(TABELMHS)) {
				char shortest_path[50];
				float downloadTime;
				
				if(!done) break;
				if(CReader.nArgument==0 || (EvaluateQueryTabelMhs(M,CReader,&done) && done)) {
					RetrieveRecordMhs(M,ACTIVENODE,EDGELIST,shortest_path,&downloadTime);
					if(downloadTime < MAXTIME) {
						if(count==0) {
							ContinuePrintProcess(); SetColor(FCI); PrintTabelMhsHeader(showNIM, showNama, showTglLahir, showKotaAsal, showIPK, showJmlSKS);
							if(detailsEnabled) {
								printf("%-15s", "Jalur");
								printf("%-7s", "Waktu");
							}
							puts("");
						}
						ContinuePrintProcess(); SetColor(FWI); PrintRecordMhs(M, showNIM, showNama, showTglLahir, showKotaAsal, showIPK, showJmlSKS);
						if(detailsEnabled) {
							printf("%-15s", shortest_path);
							printf("%-2.5f", downloadTime);
						}
						puts("");
						totalWaktu += downloadTime;
						count++;
					}
				}
			}
		}	
	} while(!done);
	
	StartPrintMsg(); SetColor(FGI); printf("Retrieve %d data selesai dalam waktu %.3f detik\n", count, totalWaktu);
}

void ParseRetrieveCommand_TabelMK(bool detailsEnabled) {
	// EARLY RETURN
	if(ISLINKEDLISTEMPTY(TABELMK)) {
		StartPrintMsg(); SetColor(FYI); puts("Database tidak memiliki satupun Record MataKuliah");
		return;
	}
	
	bool showKode = false, showNama = false, showSKS = false, showBidang = false;
	bool done;
	float totalWaktu = 0;
	int count = 0;
	ContinuePrintProcess();
	do {
		done = true;
		RestartPrintProcess(); SetColor(FWI); printf("Kolom yang akan ditampilkan : "); ReadArguments(NULL);
		if(CReader.nArgument==0) {
			showKode = true; showSKS = true; showNama = true; showBidang = true;
		}
		else {
			int i = 0;
			for(i;i<CReader.nArgument;i++) {
				if(IsStringEqual(CReader.argument[i], Kol_KodeMK)) showKode = true;
				else if(IsStringEqual(CReader.argument[i], Kol_NamaMK)) showNama = true;
				else if(IsStringEqual(CReader.argument[i], Kol_SKS)) showSKS = true;
				else if(IsStringEqual(CReader.argument[i], Kol_Bidang)) showBidang = true;
				else if(IsStringEqual(CReader.argument[i], CharToString("*"))) { 
					showKode = true; showSKS = true; showNama = true; showBidang = true;
				}
				else {
					StartPrintMsg(); printf("ERROR : Kolom %s tidak dikenali\n", CReader.argument[i].s);
					showKode = false; showSKS = false; showNama = false; showBidang = false;
					done = false;
				}
			}
		}
	} while(!done);
	
	
	do {
		done = true;
		ContinuePrintProcess(); SetColor(FWI); printf("Kondisi pencarian : "); ReadArguments(NULL);
		AddressMK M = FIRST(TABELMK);
		if(M!=NULL) {
			TRAVERSETOEND2(M,FIRST(TABELMK)) {
				char shortest_path[50];
				float downloadTime;
				
				if(!done) break;
				if(IsStringEqual(CReader.argument[0],CharToString("")) || (EvaluateQueryTabelMK(M,CReader,&done) && done)) {
					RetrieveRecordMK(M,ACTIVENODE,EDGELIST,shortest_path,&downloadTime);
					if(downloadTime < MAXTIME) {
						if(count==0) {
							ContinuePrintProcess();  SetColor(FCI); PrintTabelMKHeader(showKode, showNama, showSKS, showBidang);
							if(detailsEnabled) {
								printf("%-15s", "Jalur");
								printf("%-7s", "Waktu");
							}
							puts("");
						}
						ContinuePrintProcess(); SetColor(FWI); PrintRecordMK(M, showKode, showNama, showSKS, showBidang);
						if(detailsEnabled) {
							printf("%-15s", shortest_path);
							printf("%-2.5f", downloadTime);
						}
						puts("");
						totalWaktu += downloadTime;
						count++;
					}
				}
			}
		}
	} while(!done);
	
	StartPrintMsg(); SetColor(FGI); printf("Retrieve %d data selesai dalam waktu %.3f detik\n", count, totalWaktu);
}

void ParseRetrieveCommand_TabelPMK(bool detailsEnabled) {
	// EARLY RETURN
	if(ISLINKEDLISTEMPTY(TABELPMK)) {
		StartPrintMsg(); SetColor(FYI); puts("Database tidak memiliki satupun Record PengambilanMK");
		return;
	}
	
	bool showNIM = false, showKode = false, showTA = false, showSemester = false, showNilai = false;
	bool done;
	float totalWaktu = 0;
	int count = 0;
	ContinuePrintProcess();
	do {
		done = true;
		RestartPrintProcess(); SetColor(FWI); printf("Kolom yang akan ditampilkan : "); ReadArguments(NULL);
		if(CReader.nArgument==0) {
			showNIM = true; showKode = true; showTA = true; showSemester = true; showNilai = true;
		}
		else {
			int i = 0;
			for(i;i<CReader.nArgument;i++) {
				if(IsStringEqual(CReader.argument[i], Kol_NIM)) showNIM = true;
				else if(IsStringEqual(CReader.argument[i], Kol_KodeMK)) showKode = true;
				else if(IsStringEqual(CReader.argument[i], Kol_Semester)) showSemester = true;
				else if(IsStringEqual(CReader.argument[i], Kol_ThnAkademik)) showTA = true;
				else if(IsStringEqual(CReader.argument[i], Kol_Nilai)) showNilai = true;
				else if(IsStringEqual(CReader.argument[i], CharToString("*"))) { 
					showNIM = true; showKode = true; showTA = true; showSemester = true; showNilai = true;
				}
				else {
					StartPrintMsg(); printf("ERROR : Kolom %s tidak dikenali\n", CReader.argument[i].s);
					showNIM = false; showKode = false; showTA = false; showSemester = false; showNilai = false;
					done = false;
				}
			}
		}
	} while(!done);
	
	
	do {
		done = true;
		ContinuePrintProcess(); SetColor(FWI);printf("Kondisi pencarian : "); ReadArguments(NULL);
		AddressPMK M = FIRST(TABELPMK);
		if(M!=NULL) {
			TRAVERSETOEND2(M,FIRST(TABELPMK)) {
				char shortest_path[50];
				float downloadTime;
				
				if(!done) break;
				if(IsStringEqual(CReader.argument[0],CharToString("")) || (EvaluateQueryTabelPMK(M,CReader,&done) && done)) {
					RetrieveRecordPMK(M,ACTIVENODE,EDGELIST,shortest_path,&downloadTime);
					if(downloadTime<MAXTIME) {
						if(count==0) {
							ContinuePrintProcess(); SetColor(FCI);PrintTabelPMKHeader(showNIM, showKode, showSemester, showTA, showNilai);
							if(detailsEnabled) {
								printf("%-15s", "Jalur");
								printf("%-7s", "Waktu");
							}
							puts("");
						}
						ContinuePrintProcess(); SetColor(FWI); PrintRecordPMK(M, showNIM, showKode, showSemester, showTA, showNilai);
						if(detailsEnabled) {
							printf("%-15s", shortest_path);
							printf("%-7f", downloadTime);
						}
						puts("");
						totalWaktu += downloadTime;
						count++;
					}
				}
			}
		}
	} while(!done);
	StartPrintMsg(); SetColor(FGI); printf("Retrieve %d data selesai dalam waktu %.3f detik\n", count, totalWaktu);
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// UPDATE 
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void ParseUpdateCommand() {
	int selectedTable = 0; // 1 = mahasiswa, 2 = matakuliah, 3 = pengambilanMK, 0 = undefined
	
	while(selectedTable==0) {
		StartPrintProcess(); SetColor(FWI);
		printf("Nama Tabel : "); ReadArguments(NULL);
		if(CReader.nArgument==0) {
			StartPrintMsg(); SetColor(FRI); puts("ERROR : Perintah kosong!");
		}
		else if(IsStringEqual(ArgumentsToString(CReader),NAMATABELMHS)) selectedTable = 1;
		else if(IsStringEqual(ArgumentsToString(CReader),NAMATABELMK)) selectedTable = 2;
		else if(IsStringEqual(ArgumentsToString(CReader),NAMATABELPMK)) selectedTable = 3;
		else {
			StartPrintMsg(); SetColor(FRI); puts("ERROR : Nama tabel tidak dikenali");
			selectedTable = 0;
		}
	}
	
	switch(selectedTable) {
		case 1 : ParseUpdateCommand_TabelMhs(); break;
		case 2 : ParseUpdateCommand_TabelMK();  break;
		case 3 : ParseUpdateCommand_TabelPMK(); break;
	}
}

void ParseUpdateCommand_TabelMhs() {	
	// EARLY RETURN
	if(ISLINKEDLISTEMPTY(TABELMHS)) {
		StartPrintMsg(); SetColor(FYI); puts("Database tidak memiliki satupun Record Mahasiswa");
		return;
	}
	
	Reader query;
	string NIM, nama, kota;
	string tgllahir;
	int count, counttemp; 
	float time = 0, timetemp;
	bool valid;
	AddressMhs M;
	
	ContinuePrintProcess();
	do {
		RestartPrintProcess(); printf("Kondisi pengubahan : "); ReadArguments(NULL); query=CReader;
		valid = true;
		count = 0;
		TRAVERSETOEND2(M,FIRST(TABELMHS)) {
			if(!valid) break;
			if(query.nArgument==0 || EvaluateQueryTabelMhs(M,query,&valid)) {
				count++;
			}
		}
	} while(!valid);
	
	ContinuePrintProcess(); SetColor(FCI); puts("Perubahan Data : ");
	ContinuePrintProcess(); SetColor(FWI); printf("NIM : "); ReadArguments(NULL); NIM = ArgumentsToString(CReader);
	ContinuePrintProcess(); printf("Nama : "); ReadArguments(NULL); nama = ArgumentsToString(CReader);
	ContinuePrintProcess(); 
	do {
		RestartPrintProcess(); SetColor(FWI);
		printf("TglLahir : "); ReadArguments(NULL); tgllahir = ArgumentsToString(CReader);
		if(!IsStringDate(tgllahir) && !IsStringEqual(tgllahir,CharToString(""))) {
			StartPrintMsg(); printf("ERROR : %s bukan tanggal", tgllahir.s); 
		}
	} while(!IsStringDate(tgllahir) && !IsStringEqual(tgllahir,CharToString("")));
	ContinuePrintProcess(); printf("Kota : "); ReadArguments(NULL); kota = ArgumentsToString(CReader);
	
	if(valid) {
		time = 0;
		if(count>1 && !IsStringEqual(NIM,CharToString(""))) {
			StartPrintMsg(); SetColor(FYI); puts("Pengubahan ditolak : ditemukan lebih dari 1 record yang kolom kuncinya akan dirubah");
			count = 0;
			return;
		}
		else {
			count = 0;
			TRAVERSETOEND2(M,FIRST(TABELMHS)) {
				if(query.nArgument==0 || EvaluateQueryTabelMhs(M,query,&valid)) {
					UpdateRecordMhs(M, 	(IsStringEqual(NIM,CharToString("")) ? NIM(M) : NIM), 
										(IsStringEqual(nama,CharToString("")) ? NAMA(M) : nama), 
										(IsStringEqual(tgllahir,CharToString("")) ? TGLLAHIR(M) : StringToDate(tgllahir)), 
										(IsStringEqual(kota,CharToString("")) ? KOTAASAL(M) : kota), 
										&counttemp, &timetemp);
					ContinuePrintProcess(); SetColor(FWI); PrintRecordMhs(M,true,true,true,true,true,true); SetColor(FCI); printf("Updated");
					count += counttemp;
					time += timetemp;
				}
			}
		}
		
		StartPrintMsg(); SetColor(FGI); printf("%d record dan replikasinya berhasil diupdate dalam waktu %f detik",count,time);
	}
}

void ParseUpdateCommand_TabelMK() {	
	// EARLY RETURN
	if(ISLINKEDLISTEMPTY(TABELMK)) {
		StartPrintMsg(); SetColor(FYI); puts("Database tidak memiliki satupun Record MataKuliah");
		return;
	}
	
	Reader query;
	string Kode, nama, bidang, SKS;
	int count, counttemp; 
	float time = 0, timetemp;
	bool valid;
	AddressMK M;
	
	ContinuePrintProcess();
	do {
		RestartPrintProcess(); SetColor(FWI); printf("Kondisi pengubahan : "); ReadArguments(NULL); query=CReader;
		valid = true;
		count = 0;
		TRAVERSETOEND2(M,FIRST(TABELMK)) {
			if(!valid) break;
			if(CReader.nArgument==0 || EvaluateQueryTabelMK(M,query,&valid)) {
				count++;
			}
		}
	} while(!valid);
	
	ContinuePrintProcess(); puts("Perubahan Data : ");
	ContinuePrintProcess(); printf("Kode : "); ReadArguments(NULL); Kode = ArgumentsToString(CReader);
	ContinuePrintProcess(); printf("Nama : "); ReadArguments(NULL); nama = ArgumentsToString(CReader);
	ContinuePrintProcess(); 
	do {
		valid=true;
		RestartPrintProcess(); SetColor(FWI); printf("SKS : "); ReadArguments(NULL); SKS = ArgumentsToString(CReader);
		valid = IsStringFloat(SKS) || IsStringEqual(SKS,CharToString(""));
		if(!valid) {
			StartPrintMsg(); printf("ERROR : %s bukan angka",SKS.s);
		}
	} while(!valid);
	ContinuePrintProcess(); printf("Bidang : "); ReadArguments(NULL); bidang = ArgumentsToString(CReader);
	
	if(valid) {
		time = 0;
		if(count>1 && !IsStringEqual(Kode,CharToString(""))) {
			StartPrintMsg(); SetColor(FYI); puts("Pengubahan ditolak : ditemukan lebih dari 1 record yang kolom kuncinya akan dirubah");
			count = 0;
			return;
		}
		else {
			count = 0;
			TRAVERSETOEND2(M,FIRST(TABELMK)) {
				if(IsStringEqual(query.argument[0],CharToString("")) || EvaluateQueryTabelMK(M,query,&valid)) {
					UpdateRecordMK(M, 	(IsStringEqual(Kode,CharToString("")) ? KODEMK(M) : Kode), 
										(IsStringEqual(nama,CharToString("")) ? NAMA_MK(M) : nama), 
										(IsStringEqual(SKS,CharToString("")) ? SKS(M) : StringToInt(SKS)), 
										(IsStringEqual(bidang,CharToString("")) ? BIDANG(M) : bidang), 
										&counttemp, &timetemp);
					ContinuePrintProcess(); SetColor(FWI); PrintRecordMK(M,true,true,true,true); SetColor(FCI); printf("Updated");
					count += counttemp;
					time += timetemp;
				}
			}
			
			// update record Mhs.. JmlSKS mungkin berubah
			AddressMhs Mhs;
			TRAVERSETOEND2(Mhs,FIRST(TABELMHS))
				UpdateIPKdanJmlSKSRecordMhs(Mhs,&TABELPMK);
		}
		
		StartPrintMsg(); SetColor(FGI); printf("%d record berhasil dan replikasinya diupdate dalam waktu %f detik",count,time);
	}
}

void ParseUpdateCommand_TabelPMK() {
	// EARLY RETURN
	if(ISLINKEDLISTEMPTY(TABELMK)) {
		StartPrintMsg(); SetColor(FYI); puts("Database tidak memiliki satupun Record PengambilanMK");
		return;
	}
	
	Reader query;
	string nilai;
	int count, counttemp; 
	float time = 0, timetemp;
	bool valid;
	AddressPMK M;
	
	ContinuePrintProcess();
	do {
		RestartPrintProcess(); SetColor(FWI); printf("Kondisi pengubahan : "); ReadArguments(NULL); query=CReader;
		valid = true;
		count = 0;
		TRAVERSETOEND2(M,FIRST(TABELPMK)) {
			if(!valid) break;
			if(CReader.nArgument==0 || EvaluateQueryTabelPMK(M,query,&valid)) {
				count++;
			}
		}
	} while(!valid);
	
	ContinuePrintProcess(); puts("Perubahan Data : ");
	ContinuePrintProcess(); 
	do {
		valid = true;
		RestartPrintProcess(); SetColor(FWI);
		printf("Nilai : "); ReadArguments(NULL); nilai = ArgumentsToString(CReader);
		valid = IsStringEqual(nilai,CharToString("")) || IsStringABCDE(nilai);
		if(!valid) {
			StartPrintMsg(); SetColor(FRI); printf("%s tidak valid, harus salah satu antara A, B, C, D, atau E", nilai.s);
		}
	} while(!valid);
	
	if(valid) {
		time = 0;
		if(false) {
		
		}
		else {
			count = 0;
			TRAVERSETOEND2(M,FIRST(TABELPMK)) {
				if(IsStringEqual(query.argument[0],CharToString("")) || EvaluateQueryTabelPMK(M,query,&valid)) {
					UpdateRecordPMK(M, 	MHS(M), 
										MK(M), 
										SEMESTER(M), 
										TAHUN(M),
										(IsStringEqual(nilai,CharToString("")) ? NILAI(M) : *StringToChar(nilai)), 
										&counttemp, &timetemp);
					ContinuePrintProcess(); SetColor(FWI); PrintRecordPMK(M,true,true,true,true,true); SetColor(FCI); printf("Updated");
					count += counttemp;
					time += timetemp;
				}
			}
			// update record Mhs.. IPK mungkin berubah
			AddressMhs Mhs;
			TRAVERSETOEND2(Mhs,FIRST(TABELMHS))
				UpdateIPKdanJmlSKSRecordMhs(Mhs,&TABELPMK);
		}
		
		StartPrintMsg(); SetColor(FGI); printf("%d record berhasil dan replikasinya diupdate dalam waktu %f detik",count,time);
	}
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// DELETE
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void ParseDeleteCommand() {
	int selectedTable = 0; // 1 = mahasiswa, 2 = matakuliah, 3 = pengambilanMK, 0 = undefined
	
	while(selectedTable==0) {
		StartPrintProcess(); SetColor(FWI);
		printf("Nama Tabel : "); ReadArguments(NULL);
		if(CReader.nArgument==0) {
			StartPrintMsg(); SetColor(FRI); puts("ERROR : Perintah kosong!");
		}
		else if(IsStringEqual(ArgumentsToString(CReader),NAMATABELMHS)) selectedTable = 1;
		else if(IsStringEqual(ArgumentsToString(CReader),NAMATABELMK)) selectedTable = 2;
		else if(IsStringEqual(ArgumentsToString(CReader),NAMATABELPMK)) selectedTable = 3;
		else {
			StartPrintMsg(); SetColor(FRI); puts("ERROR : Nama tabel tidak dikenali");
			selectedTable = 0;
		}
	}
	
	switch(selectedTable) {
		case 1 : ParseDeleteCommand_TabelMhs(); break;
		case 2 : ParseDeleteCommand_TabelMK();  break;
		case 3 : ParseDeleteCommand_TabelPMK(); break;
	}
}

void ParseDeleteCommand_TabelMhs() {
	// EARLY RETURN
	if(ISLINKEDLISTEMPTY(TABELMHS)) {
		StartPrintMsg(); SetColor(FYI); puts("Tabel Kosong");
		return;
	}
	// END EARLY RETURN

	int count, counttemp;
	float time, timetemp;
	bool valid;
	AddressMhs M;
	AddressMhs next;
	
	ContinuePrintProcess();
	do {
		count = 0; time = 0; valid = true;
		RestartPrintProcess(); SetColor(FWI); printf("Kondisi penghapusan : "); ReadArguments(NULL);
		TRAVERSETOEND3(M,next,FIRST(TABELMHS)) {
			if(!valid) break;
			if(CReader.nArgument==0 || EvaluateQueryTabelMhs(M,CReader,&valid)) {
				ContinuePrintProcess(); SetColor(FWI); PrintRecordMhs(M,true,true,true,true,true,true); SetColor(FCI); printf("Deleted");
				DeleteRecordMhs(M, &TABELMHS, &TABELPMK, &timetemp, &counttemp);
				time += timetemp;
				count += counttemp;
			}
		}
	} while(!valid);
	
	StartPrintMsg(); SetColor(FGI); printf("%d record dan replikasinya dihapus dalam waktu %f detik\n", count, time);
}

void ParseDeleteCommand_TabelMK() {
	// EARLY RETURN
	if(ISLINKEDLISTEMPTY(TABELMK)) {
		StartPrintMsg(); SetColor(FYI); puts("Tabel Kosong");
		return;
	}
	// END EARLY RETURN
	
	int count, counttemp;
	float time, timetemp;
	bool valid;
	AddressMK M;
	AddressMK next;
	
	ContinuePrintProcess();
	do {
		RestartPrintProcess(); SetColor(FWI); count = 0; time = 0; valid = true;
		printf("Kondisi penghapusan : "); ReadArguments(NULL);
		TRAVERSETOEND3(M,next,FIRST(TABELMK)) {
			if(!valid) break;
			if(CReader.nArgument==0 || EvaluateQueryTabelMK(M,CReader,&valid)) {
				ContinuePrintProcess(); SetColor(FWI); PrintRecordMK(M,true,true,true,true); SetColor(FCI); printf("Deleted");
				DeleteRecordMK(M, &TABELMK, &TABELPMK, &timetemp, &counttemp);
				time += timetemp;
				count += counttemp;
			}
		}
	} while(!valid);
	
	StartPrintMsg(); SetColor(FGI); printf("%d record dan replikasinya dihapus dalam waktu %f detik\n", count, time);
}

void ParseDeleteCommand_TabelPMK() {
	// EARLY RETURN
	if(ISLINKEDLISTEMPTY(TABELPMK)) {
		StartPrintMsg(); SetColor(FYI); puts("Tabel Kosong");
		return;
	}
	// END EARLY RETURN
	
	int count, counttemp;
	float time, timetemp;
	bool valid;
	AddressPMK M;
	AddressPMK next;
	
	ContinuePrintProcess();
	do {
		RestartPrintProcess();
		count = 0; time = 0; valid = true;
		printf("Kondisi penghapusan : "); ReadArguments(NULL);
		TRAVERSETOEND3(M,next,FIRST(TABELPMK)) {
			if(!valid) break;
			if(CReader.nArgument==0 || EvaluateQueryTabelPMK(M,CReader,&valid)) {
				ContinuePrintProcess(); SetColor(FWI); PrintRecordPMK(M,true,true,true,true,true); SetColor(FCI); printf("Deleted");
				DeleteRecordPMK(M, &TABELPMK, &timetemp, &counttemp);
				time += timetemp;
				count += counttemp;
			}
		}
	} while(!valid);
	StartPrintMsg(); SetColor(FGI); printf("%d record dan replikasinya dihapus dalam waktu %f detik\n", count, time);
}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// EXIT / HELP
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void ParseExitCommand_SimulationMode() {
	ChangeModus(PERSIAPAN);
}

void ParseHelpCommand_SimulationMode() {
	StartPrintProcess(); printf("Perintah yang tersedia : ");
	ContinuePrintProcess(); printf("  Retrieve");
	ContinuePrintProcess(); printf("  Insert");
	ContinuePrintProcess(); printf("  Update");
	ContinuePrintProcess(); printf("  Delete");
}



